﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.DataServices.WKT.Support;

namespace Petra.Gis.DataServices.WKT
{
  public class RegexGeometryParser
  {

    private Dictionary<string, int> _GroupPositions;

    Regex r;
    Match m;
    //WKTGeometryType _geomType;

    private static Dictionary<WKTGeometryType, string> getRegex;
    /*
     * static constructor initiate internal dictionary with regex patterns for each geometry type
     * single place init is better for support
     */
    static RegexGeometryParser() 
    {
      string pattern;

      getRegex = new Dictionary<WKTGeometryType, string>();

      //Point regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.Point] + ")" + //5-18 chars
        @"(?<geometry>\s*\()" + //cordlist
        @"(?<coordlist>(\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?){2}" +
        @"(?<geometry>\s*\))"; //+ //cordlist end
          //@"(?<invalid>[^\s]+)"; //put all regex exps here to avoid invalid chars in input string

      getRegex.Add(WKTGeometryType.Point, pattern);



      //LineString regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.LineString] + ")" +
        @"(?<geometry>\s*\()" +
        //@"(?<coordlist>(\s*|\s*\,{1}\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?)+" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry>\s*\))"; //+
      //@"(?<invalid>[^\s]+)"; //put all regex exps here to avoid invalid chars in input string

      getRegex.Add(WKTGeometryType.LineString, pattern);



      //Polygon regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.Polygon] + ")" +
        @"(?<geometry>\s*\()" +
        @"((?<rseparator>\s*|\s*\,{1}\s*|\s+)(?<geometry1>\()" +
        //@"(?<coordlist>(\s*|\s*\,{1}\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?)+" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry1>\s*\)))+" +
        @"(?<geometry>\s*\))"; // +

      getRegex.Add(WKTGeometryType.Polygon, pattern);



      //MultiPoint regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.MultiPoint] + ")" +
        @"(?<geometry>\s*\({1})" +
        //@"(?<coordlist>(\s*|\s*\,{1}\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?)+" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry>\s*\){1})"; //+
      //@"(?<invalid>[^\s]+)"; //put all regex exps here to avoid invalid chars in input string

      getRegex.Add(WKTGeometryType.MultiPoint, pattern);



      //MultiLine regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.MultiLineString] + ")" +
        @"(?<geometry>\s*\()" +
        @"((?<sseparator>\s*|\s*\,{1}\s*|\s+)(?<geometry1>\()" +
        //@"(?<coordlist>(\s*|\s*\,{1}\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?)+" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry1>\s*\)))+" +
        @"(?<geometry>\s*\))"; // +
        //@"(?<invalid>[^\s]+)"; //put all regex exps here to avoid invalid chars in input string

      getRegex.Add(WKTGeometryType.MultiLineString, pattern);



      //MultiPolygon regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.MultiPolygon] + ")" +
        @"(?<geometry>\s*\()" +
        @"((?<pseparator>\s*|\s*\,{1}\s*|\s+)(?<geometry1>\()" +
        @"((?<rseparator>\s*|\s*\,{1}\s*|\s+)(?<geometry2>\()" +
        //@"(?<coordlist>(\s*|\s*\,{1}\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?)+" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry2>\s*\)))+" +
        @"(?<geometry1>\s*\)))+" +
        @"(?<geometry>\s*\))"; //+
        //@"(?<invalid>[^\s]+)"; //put all regex exps here to avoid invalid chars in input string

      getRegex.Add(WKTGeometryType.MultiPolygon, pattern);


      //GeometryCollection regex pattern
      pattern = @"(?<whitespace>\s*)" +
        @"(?<geometryname>" + WKT.GeometryFactory.getWktName[WKTGeometryType.GeometryCollection] + @")" +
        @"(?<geometry>\s*\()" +

        @"((?<gseparator>\s*|\s*\,{1}\s*|\s+)" +

        //point, linestring, same regex, only name differs
        @"(?<geometryname>\s*" + WKT.GeometryFactory.getWktName[WKTGeometryType.Point] + @"|\s*" + 
        WKT.GeometryFactory.getWktName[WKTGeometryType.LineString] + ")" +
        @"(?<geometry1>\s*\()" + //cordlist
        //@"(?<coordlist>(\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?){2}" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry1>\s*\))" + //cordlist end

        @"|" +

        //polygon
        @"(?<geometryname>\s*" + WKT.GeometryFactory.getWktName[WKTGeometryType.Polygon] + @")" +
        @"(?<geometry1>\s*\()" +
        @"((?<rseparator>\s*|\s*\,{1}\s*|\s+)(?<geometry2>\()" +
        //@"(?<coordlist>(\s*|\s*\,{1}\s*|\s+)\-?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?)+" +
        @"((?<separator>\s*|\s*\,{1}\s*|\s+)(?<coordlist>(\-?\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?))+" +
        @"(?<geometry2>\s*\)))+" +
        @"(?<geometry1>\s*\))" +

        @")+" +

        @"(?<geometry>\s*\))"; // +
      //@"(?<invalid>[^\s]+)"; //put all regex exps here to avoid invalid chars in input string

      getRegex.Add(WKTGeometryType.GeometryCollection, pattern);


    }

    public RegexGeometryParser(WKTGeometryType geomType)
    {

      string pattern = RegexGeometryParser.getRegex[geomType];
      r = new Regex(pattern);
      //_geomType = geomType;

    }

    public RegexGeometry ParseGeometry(ref string inputStr)
    {

      m = r.Match(inputStr);

      if (m.Length < 1)
        throw new ArgumentException("invalid input string for wkt", "inputStr");

      if((m.Groups["coordinates"].Captures.Count % 2) != 0)
        throw new ArgumentException("invalid input string for wkt", "inputStr");


      _GroupPositions = new Dictionary<string,int>();

      RegexGeometry rg = new RegexGeometry(this);

      return rg;

    }

    public Match regexMatch
    {
      get { return m; }
    }

    public int getStartByCapture(string GroupName, Capture c)
    {
      int startPos;

      try { startPos = _GroupPositions[GroupName]; }
      catch { _GroupPositions.Add(GroupName, 0); startPos = 0; }

      CaptureCollection cc = m.Groups[GroupName].Captures;

      int _start;
      for (_start = startPos; _start < cc.Count; _start++)
        if (c.Index < cc[_start].Index)
          break;

      _GroupPositions[GroupName] = _start;
      return _start;

    }

    public int getEndByCapture(string GroupName, Capture c)
    {

      int startPos;

      try { startPos = _GroupPositions[GroupName]; }
      catch { _GroupPositions.Add(GroupName, 0); startPos = 0; }

      CaptureCollection cc = m.Groups[GroupName].Captures;

      int _end;

      //get end coord index
      for (_end = startPos; _end < cc.Count; _end++)
        if (c.Index < cc[_end].Index)
          break;

      _end--;
      _GroupPositions[GroupName] = _end;
      return _end;
    }

  }



}
